/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.catalina.core;


import org.apache.catalina.*;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.mbeans.MBeanUtils;
import org.apache.catalina.util.LifecycleMBeanBase;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.naming.resources.ProxyDirContext;
import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.MultiThrowable;
import org.apache.tomcat.util.res.StringManager;

import javax.management.ObjectName;
import javax.naming.directory.DirContext;
import javax.servlet.ServletException;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.IOException;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;


/**
 * Abstract implementation of the <b>Container</b> interface, providing common
 * functionality required by nearly every implementation.  Classes extending
 * this base class must implement <code>getInfo()</code>, and may implement
 * a replacement for <code>invoke()</code>.
 * <p>
 * All subclasses of this abstract base class will include support for a
 * Pipeline object that defines the processing to be performed for each request
 * received by the <code>invoke()</code> method of this class, utilizing the
 * "Chain of Responsibility" design pattern.  A subclass should encapsulate its
 * own processing functionality as a <code>Valve</code>, and configure this
 * Valve into the pipeline by calling <code>setBasic()</code>.
 * <p>
 * This implementation fires property change events, per the JavaBeans design
 * pattern, for changes in singleton properties.  In addition, it fires the
 * following <code>ContainerEvent</code> events to listeners who register
 * themselves with <code>addContainerListener()</code>:
 * <table border=1>
 * <tr>
 * <th>Type</th>
 * <th>Data</th>
 * <th>Description</th>
 * </tr>
 * <tr>
 * <td align=center><code>addChild</code></td>
 * <td align=center><code>Container</code></td>
 * <td>Child container added to this Container.</td>
 * </tr>
 * <tr>
 * <td align=center><code>{@link #getPipeline() pipeline}.addValve</code></td>
 * <td align=center><code>Valve</code></td>
 * <td>Valve added to this Container.</td>
 * </tr>
 * <tr>
 * <td align=center><code>removeChild</code></td>
 * <td align=center><code>Container</code></td>
 * <td>Child container removed from this Container.</td>
 * </tr>
 * <tr>
 * <td align=center><code>{@link #getPipeline() pipeline}.removeValve</code></td>
 * <td align=center><code>Valve</code></td>
 * <td>Valve removed from this Container.</td>
 * </tr>
 * <tr>
 * <td align=center><code>start</code></td>
 * <td align=center><code>null</code></td>
 * <td>Container was started.</td>
 * </tr>
 * <tr>
 * <td align=center><code>stop</code></td>
 * <td align=center><code>null</code></td>
 * <td>Container was stopped.</td>
 * </tr>
 * </table>
 * Subclasses that fire additional events should document them in the
 * class comments of the implementation class.
 * <p>
 * TODO: Review synchronisation around background processing. See bug 47024.
 *
 * @author Craig R. McClanahan
 */
public abstract class ContainerBase extends LifecycleMBeanBase
		implements Container {

	private static final org.apache.juli.logging.Log log =
			org.apache.juli.logging.LogFactory.getLog(ContainerBase.class);

	/**
	 * Perform addChild with the permissions of this class.
	 * addChild can be called with the XML parser on the stack,
	 * this allows the XML parser to have fewer privileges than
	 * Tomcat.
	 */
	protected class PrivilegedAddChild
			implements PrivilegedAction<Void> {

		private Container child;

		PrivilegedAddChild(Container child) {
			this.child = child;
		}

		@Override
		public Void run() {
			addChildInternal(child);
			return null;
		}

	}


	// ----------------------------------------------------- Instance Variables


	/**
	 * The child Containers belonging to this Container, keyed by name.
	 */
	protected HashMap<String, Container> children =
			new HashMap<String, Container>();


	/**
	 * The processor delay for this component.
	 */
	protected int backgroundProcessorDelay = -1;


	/**
	 * The container event listeners for this Container. Implemented as a
	 * CopyOnWriteArrayList since listeners may invoke methods to add/remove
	 * themselves or other listeners and with a ReadWriteLock that would trigger
	 * a deadlock.
	 */
	protected List<ContainerListener> listeners =
			new CopyOnWriteArrayList<ContainerListener>();

	/**
	 * The Loader implementation with which this Container is associated.
	 */
	protected Loader loader = null;
	private final ReadWriteLock loaderLock = new ReentrantReadWriteLock();


	/**
	 * The Logger implementation with which this Container is associated.
	 */
	protected Log logger = null;


	/**
	 * Associated logger name.
	 */
	protected String logName = null;


	/**
	 * The Manager implementation with which this Container is associated.
	 */
	protected Manager manager = null;
	private final ReadWriteLock managerLock = new ReentrantReadWriteLock();


	/**
	 * The cluster with which this Container is associated.
	 */
	protected Cluster cluster = null;
	private final ReadWriteLock clusterLock = new ReentrantReadWriteLock();


	/**
	 * The human-readable name of this Container.
	 */
	protected String name = null;


	/**
	 * The parent Container to which this Container is a child.
	 */
	protected Container parent = null;


	/**
	 * The parent class loader to be configured when we install a Loader.
	 */
	protected ClassLoader parentClassLoader = null;


	/**
	 * The Pipeline object with which this Container is associated.
	 */
	protected Pipeline pipeline = new StandardPipeline(this);


	/**
	 * The Realm with which this Container is associated.
	 */
	private volatile Realm realm = null;


	/**
	 * Lock used to control access to the Realm.
	 */
	private final ReadWriteLock realmLock = new ReentrantReadWriteLock();

	/**
	 * The resources DirContext object with which this Container is associated.
	 */
	protected DirContext resources = null;
	private final ReadWriteLock resourcesLock = new ReentrantReadWriteLock();


	/**
	 * The string manager for this package.
	 */
	protected static final StringManager sm =
			StringManager.getManager(Constants.Package);


	/**
	 * Will children be started automatically when they are added.
	 */
	protected boolean startChildren = true;

	/**
	 * The property change support for this component.
	 */
	protected PropertyChangeSupport support = new PropertyChangeSupport(this);


	/**
	 * The background thread.
	 */
	private Thread thread = null;


	/**
	 * The background thread completion semaphore.
	 */
	private volatile boolean threadDone = false;


	/**
	 * The access log to use for requests normally handled by this container
	 * that have been handled earlier in the processing chain.
	 */
	protected volatile AccessLog accessLog = null;
	private volatile boolean accessLogScanComplete = false;


	/**
	 * The number of threads available to process start and stop events for any
	 * children associated with this container.
	 */
	private int startStopThreads = 1;
	/**
	 * 线程池
	 */
	protected ThreadPoolExecutor startStopExecutor;

	// ------------------------------------------------------------- Properties

	@Override
	public int getStartStopThreads() {
		return startStopThreads;
	}

	/**
	 * Handles the special values.
	 */
	private int getStartStopThreadsInternal() {
		int result = getStartStopThreads();

		// Positive values are unchanged
		if (result > 0) {
			return result;
		}

		// Zero == Runtime.getRuntime().availableProcessors()
		// -ve  == Runtime.getRuntime().availableProcessors() + value
		// These two are the same
		result = Runtime.getRuntime().availableProcessors() + result;
		if (result < 1) {
			result = 1;
		}
		return result;
	}

	@Override
	public void setStartStopThreads(int startStopThreads) {
		this.startStopThreads = startStopThreads;

		// Use local copies to ensure thread safety
		ThreadPoolExecutor executor = startStopExecutor;
		if (executor != null) {
			int newThreads = getStartStopThreadsInternal();
			executor.setMaximumPoolSize(newThreads);
			executor.setCorePoolSize(newThreads);
		}
	}


	/**
	 * Get the delay between the invocation of the backgroundProcess method on
	 * this container and its children. Child containers will not be invoked
	 * if their delay value is not negative (which would mean they are using
	 * their own thread). Setting this to a positive value will cause
	 * a thread to be spawn. After waiting the specified amount of time,
	 * the thread will invoke the executePeriodic method on this container
	 * and all its children.
	 */
	@Override
	public int getBackgroundProcessorDelay() {
		return backgroundProcessorDelay;
	}


	/**
	 * Set the delay between the invocation of the execute method on this
	 * container and its children.
	 *
	 * @param delay The delay in seconds between the invocation of
	 *              backgroundProcess methods
	 */
	@Override
	public void setBackgroundProcessorDelay(int delay) {
		backgroundProcessorDelay = delay;
	}


	/**
	 * Return descriptive information about this Container implementation and
	 * the corresponding version number, in the format
	 * <code>&lt;description&gt;/&lt;version&gt;</code>.
	 */
	@Override
	public String getInfo() {
		return this.getClass().getName();
	}


	/**
	 * Return the Loader with which this Container is associated.  If there is
	 * no associated Loader, return the Loader associated with our parent
	 * Container (if any); otherwise, return <code>null</code>.
	 */
	@Override
	public Loader getLoader() {
		Lock readLock = loaderLock.readLock();
		readLock.lock();
		try {
			if (loader != null)
				return loader;

			if (parent != null)
				return parent.getLoader();

			return null;
		} finally {
			readLock.unlock();
		}
	}


	/*
	 * Provide access to just the loader component attached to this container.
	 */
	protected Loader getLoaderInternal() {
		Lock readLock = loaderLock.readLock();
		readLock.lock();
		try {
			return loader;
		} finally {
			readLock.unlock();
		}
	}


	/**
	 * Set the Loader with which this Container is associated.
	 *
	 * @param loader The newly associated loader
	 */
	@Override
	public void setLoader(Loader loader) {
		Lock writeLock = loaderLock.writeLock();
		writeLock.lock();
		Loader oldLoader = null;
		try {
			// Change components if necessary
			oldLoader = this.loader;
			if (oldLoader == loader)
				return;
			this.loader = loader;

			// Stop the old component if necessary
			if (getState().isAvailable() && (oldLoader != null) &&
					(oldLoader instanceof Lifecycle)) {
				try {
					((Lifecycle) oldLoader).stop();
				} catch (LifecycleException e) {
					log.error("ContainerBase.setLoader: stop: ", e);
				}
			}

			// Start the new component if necessary
			if (loader != null)
				loader.setContainer(this);
			if (getState().isAvailable() && (loader != null) &&
					(loader instanceof Lifecycle)) {
				try {
					((Lifecycle) loader).start();
				} catch (LifecycleException e) {
					log.error("ContainerBase.setLoader: start: ", e);
				}
			}
		} finally {
			writeLock.unlock();
		}

		// Report this property change to interested listeners
		support.firePropertyChange("loader", oldLoader, loader);

	}


	/**
	 * Return the Logger for this Container.
	 */
	@Override
	public Log getLogger() {

		if (logger != null)
			return (logger);
		logger = LogFactory.getLog(logName());
		return (logger);

	}


	/**
	 * Return the Manager with which this Container is associated.  If there is
	 * no associated Manager, return the Manager associated with our parent
	 * Container (if any); otherwise return <code>null</code>.
	 */
	@Override
	public Manager getManager() {
		Lock readLock = managerLock.readLock();
		readLock.lock();
		try {
			if (manager != null)
				return manager;

			if (parent != null)
				return parent.getManager();

			return null;
		} finally {
			readLock.unlock();
		}
	}


	/*
	 * Provide access to just the manager component attached to this container.
	 */
	protected Manager getManagerInternal() {
		Lock readLock = managerLock.readLock();
		readLock.lock();
		try {
			return manager;
		} finally {
			readLock.unlock();
		}
	}


	/**
	 * Set the Manager with which this Container is associated.
	 *
	 * @param manager The newly associated Manager
	 */
	@Override
	public void setManager(Manager manager) {
		Lock writeLock = managerLock.writeLock();
		writeLock.lock();
		Manager oldManager = null;
		try {
			// Change components if necessary
			oldManager = this.manager;
			if (oldManager == manager)
				return;
			this.manager = manager;

			// Stop the old component if necessary
			if (oldManager instanceof Lifecycle) {
				try {
					((Lifecycle) oldManager).stop();
					((Lifecycle) oldManager).destroy();
				} catch (LifecycleException e) {
					log.error("ContainerBase.setManager: stop-destroy: ", e);
				}
			}

			// Start the new component if necessary
			if (manager != null)
				manager.setContainer(this);
			if (getState().isAvailable() && manager instanceof Lifecycle) {
				try {
					((Lifecycle) manager).start();
				} catch (LifecycleException e) {
					log.error("ContainerBase.setManager: start: ", e);
				}
			}
		} finally {
			writeLock.unlock();
		}

		// Report this property change to interested listeners
		support.firePropertyChange("manager", oldManager, manager);
	}


	/**
	 * Return an object which may be utilized for mapping to this component.
	 */
	@Deprecated
	@Override
	public Object getMappingObject() {
		return this;
	}


	/**
	 * Return the Cluster with which this Container is associated.  If there is
	 * no associated Cluster, return the Cluster associated with our parent
	 * Container (if any); otherwise return <code>null</code>.
	 */
	@Override
	public Cluster getCluster() {
		Lock readLock = clusterLock.readLock();
		readLock.lock();
		try {
			if (cluster != null)
				return cluster;

			if (parent != null)
				return parent.getCluster();

			return null;
		} finally {
			readLock.unlock();
		}
	}


	/*
	 * Provide access to just the cluster component attached to this container.
	 */
	protected Cluster getClusterInternal() {
		Lock readLock = clusterLock.readLock();
		readLock.lock();
		try {
			return cluster;
		} finally {
			readLock.unlock();
		}
	}


	/**
	 * Set the Cluster with which this Container is associated.
	 *
	 * @param cluster The newly associated Cluster
	 */
	@Override
	public void setCluster(Cluster cluster) {
		Cluster oldCluster = null;
		Lock writeLock = clusterLock.writeLock();
		writeLock.lock();
		try {
			// Change components if necessary
			oldCluster = this.cluster;
			if (oldCluster == cluster)
				return;
			this.cluster = cluster;

			// Stop the old component if necessary
			if (getState().isAvailable() && (oldCluster != null) &&
					(oldCluster instanceof Lifecycle)) {
				try {
					((Lifecycle) oldCluster).stop();
				} catch (LifecycleException e) {
					log.error("ContainerBase.setCluster: stop: ", e);
				}
			}

			// Start the new component if necessary
			if (cluster != null)
				cluster.setContainer(this);

			if (getState().isAvailable() && (cluster != null) &&
					(cluster instanceof Lifecycle)) {
				try {
					((Lifecycle) cluster).start();
				} catch (LifecycleException e) {
					log.error("ContainerBase.setCluster: start: ", e);
				}
			}
		} finally {
			writeLock.unlock();
		}

		// Report this property change to interested listeners
		support.firePropertyChange("cluster", oldCluster, cluster);
	}


	/**
	 * Return a name string (suitable for use by humans) that describes this
	 * Container.  Within the set of child containers belonging to a particular
	 * parent, Container names must be unique.
	 */
	@Override
	public String getName() {

		return (name);

	}


	/**
	 * Set a name string (suitable for use by humans) that describes this
	 * Container.  Within the set of child containers belonging to a particular
	 * parent, Container names must be unique.
	 *
	 * @param name New name of this container
	 * @throws IllegalStateException if this Container has already been
	 *                               added to the children of a parent Container (after which the name
	 *                               may not be changed)
	 */
	@Override
	public void setName(String name) {
		if (name == null) {
			throw new IllegalArgumentException(sm.getString("containerBase.nullName"));
		}
		String oldName = this.name;
		this.name = name;
		support.firePropertyChange("name", oldName, this.name);
	}


	/**
	 * Return if children of this container will be started automatically when
	 * they are added to this container.
	 */
	public boolean getStartChildren() {

		return (startChildren);

	}


	/**
	 * Set if children of this container will be started automatically when
	 * they are added to this container.
	 *
	 * @param startChildren New value of the startChildren flag
	 */
	public void setStartChildren(boolean startChildren) {

		boolean oldStartChildren = this.startChildren;
		this.startChildren = startChildren;
		support.firePropertyChange("startChildren", oldStartChildren, this.startChildren);
	}


	/**
	 * Return the Container for which this Container is a child, if there is
	 * one.  If there is no defined parent, return <code>null</code>.
	 */
	@Override
	public Container getParent() {

		return (parent);

	}


	/**
	 * Set the parent Container to which this Container is being added as a
	 * child.  This Container may refuse to become attached to the specified
	 * Container by throwing an exception.
	 *
	 * @param container Container to which this Container is being added
	 *                  as a child
	 * @throws IllegalArgumentException if this Container refuses to become
	 *                                  attached to the specified Container
	 */
	@Override
	public void setParent(Container container) {

		Container oldParent = this.parent;
		this.parent = container;
		support.firePropertyChange("parent", oldParent, this.parent);

	}


	/**
	 * Return the parent class loader (if any) for this web application.
	 * This call is meaningful only <strong>after</strong> a Loader has
	 * been configured.
	 */
	@Override
	public ClassLoader getParentClassLoader() {
		if (parentClassLoader != null)
			return (parentClassLoader);
		if (parent != null) {
			return (parent.getParentClassLoader());
		}
		return (ClassLoader.getSystemClassLoader());

	}


	/**
	 * Set the parent class loader (if any) for this web application.
	 * This call is meaningful only <strong>before</strong> a Loader has
	 * been configured, and the specified value (if non-null) should be
	 * passed as an argument to the class loader constructor.
	 *
	 * @param parent The new parent class loader
	 */
	@Override
	public void setParentClassLoader(ClassLoader parent) {
		ClassLoader oldParentClassLoader = this.parentClassLoader;
		this.parentClassLoader = parent;
		support.firePropertyChange("parentClassLoader", oldParentClassLoader,
				this.parentClassLoader);

	}


	/**
	 * Return the Pipeline object that manages the Valves associated with
	 * this Container.
	 */
	@Override
	public Pipeline getPipeline() {

		return (this.pipeline);

	}


	/**
	 * Return the Realm with which this Container is associated.  If there is
	 * no associated Realm, return the Realm associated with our parent
	 * Container (if any); otherwise return <code>null</code>.
	 */
	@Override
	public Realm getRealm() {

		Lock l = realmLock.readLock();
		try {
			l.lock();
			if (realm != null)
				return (realm);
			if (parent != null)
				return (parent.getRealm());
			return null;
		} finally {
			l.unlock();
		}
	}


	protected Realm getRealmInternal() {
		Lock l = realmLock.readLock();
		try {
			l.lock();
			return realm;
		} finally {
			l.unlock();
		}
	}

	/**
	 * Set the Realm with which this Container is associated.
	 *
	 * @param realm The newly associated Realm
	 */
	@Override
	public void setRealm(Realm realm) {

		Lock l = realmLock.writeLock();

		try {
			l.lock();

			// Change components if necessary
			Realm oldRealm = this.realm;
			if (oldRealm == realm)
				return;
			this.realm = realm;

			// Stop the old component if necessary
			if (getState().isAvailable() && (oldRealm != null) &&
					(oldRealm instanceof Lifecycle)) {
				try {
					((Lifecycle) oldRealm).stop();
				} catch (LifecycleException e) {
					log.error("ContainerBase.setRealm: stop: ", e);
				}
			}

			// Start the new component if necessary
			if (realm != null)
				realm.setContainer(this);
			if (getState().isAvailable() && (realm != null) &&
					(realm instanceof Lifecycle)) {
				try {
					((Lifecycle) realm).start();
				} catch (LifecycleException e) {
					log.error("ContainerBase.setRealm: start: ", e);
				}
			}

			// Report this property change to interested listeners
			support.firePropertyChange("realm", oldRealm, this.realm);
		} finally {
			l.unlock();
		}

	}


	/**
	 * Return the resources DirContext object with which this Container is
	 * associated.  If there is no associated resources object, return the
	 * resources associated with our parent Container (if any); otherwise
	 * return <code>null</code>.
	 */
	@Override
	public DirContext getResources() {
		Lock readLock = resourcesLock.readLock();
		readLock.lock();
		try {
			if (resources != null)
				return resources;

			if (parent != null)
				return parent.getResources();

			return null;
		} finally {
			readLock.unlock();
		}
	}


	/*
	 * Provide access to just the resources component attached to this container.
	 */
	protected DirContext getResourcesInternal() {
		Lock readLock = resourcesLock.readLock();
		readLock.lock();
		try {
			return resources;
		} finally {
			readLock.unlock();
		}
	}


	/**
	 * Set the resources DirContext object with which this Container is
	 * associated.
	 *
	 * @param resources The newly associated DirContext
	 */
	@Override
	public void setResources(DirContext resources) {
		// Called from StandardContext.setResources()
		//              <- StandardContext.start()
		//              <- ContainerBase.addChildInternal()
		Lock writeLock = resourcesLock.writeLock();
		writeLock.lock();
		DirContext oldResources = null;
		try {
			// Change components if necessary
			oldResources = this.resources;
			if (oldResources == resources)
				return;
			// null resources don't need to be wrapped. Neither do resources
			// that are already wrapped.
			if (resources == null || resources instanceof ProxyDirContext) {
				this.resources = resources;
			} else {
				Hashtable<String, String> env = new Hashtable<String, String>();
				if (getParent() != null)
					env.put(ProxyDirContext.HOST, getParent().getName());
				env.put(ProxyDirContext.CONTEXT, getName());
				this.resources = new ProxyDirContext(env, resources);
			}
		} finally {
			writeLock.unlock();
		}

		// Report this property change to interested listeners
		support.firePropertyChange("resources", oldResources, resources);
	}


	// ------------------------------------------------------ Container Methods


	/**
	 * Add a new child Container to those associated with this Container,
	 * if supported.  Prior to adding this Container to the set of children,
	 * the child's <code>setParent()</code> method must be called, with this
	 * Container as an argument.  This method may thrown an
	 * <code>IllegalArgumentException</code> if this Container chooses not
	 * to be attached to the specified Container, in which case it is not added
	 *
	 * @param child New child Container to be added
	 * @throws IllegalArgumentException if this exception is thrown by
	 *                                  the <code>setParent()</code> method of the child Container
	 * @throws IllegalArgumentException if the new child does not have
	 *                                  a name unique from that of existing children of this Container
	 * @throws IllegalStateException    if this Container does not support
	 *                                  child Containers
	 */
	@Override
	public void addChild(Container child) {
		if (Globals.IS_SECURITY_ENABLED) {
			PrivilegedAction<Void> dp =
					new PrivilegedAddChild(child);
			AccessController.doPrivileged(dp);
		} else {
			addChildInternal(child);
		}
	}

	private void addChildInternal(Container child) {

		if (log.isDebugEnabled())
			log.debug("Add child " + child + " " + this);
		synchronized (children) {
			if (children.get(child.getName()) != null)
				throw new IllegalArgumentException("addChild:  Child name '" +
						child.getName() +
						"' is not unique");
			child.setParent(this);  // May throw IAE
			children.put(child.getName(), child);
		}

		// Start child
		// Don't do this inside sync block - start can be a slow process and
		// locking the children object can cause problems elsewhere
		try {
			if ((getState().isAvailable() ||
					LifecycleState.STARTING_PREP.equals(getState())) &&
					startChildren) {
				child.start();
			}
		} catch (LifecycleException e) {
			log.error("ContainerBase.addChild: start: ", e);
			throw new IllegalStateException("ContainerBase.addChild: start: " + e);
		} finally {
			fireContainerEvent(ADD_CHILD_EVENT, child);
		}
	}


	/**
	 * Add a container event listener to this component.
	 *
	 * @param listener The listener to add
	 */
	@Override
	public void addContainerListener(ContainerListener listener) {
		listeners.add(listener);
	}


	/**
	 * Add a property change listener to this component.
	 *
	 * @param listener The listener to add
	 */
	@Override
	public void addPropertyChangeListener(PropertyChangeListener listener) {

		support.addPropertyChangeListener(listener);

	}


	/**
	 * Return the child Container, associated with this Container, with
	 * the specified name (if any); otherwise, return <code>null</code>
	 *
	 * @param name Name of the child Container to be retrieved
	 */
	@Override
	public Container findChild(String name) {

		if (name == null)
			return (null);
		synchronized (children) {
			return children.get(name);
		}

	}


	/**
	 * Return the set of children Containers associated with this Container.
	 * If this Container has no children, a zero-length array is returned.
	 */
	@Override
	public Container[] findChildren() {

		synchronized (children) {
			Container results[] = new Container[children.size()];
			return children.values().toArray(results);
		}

	}


	/**
	 * Return the set of container listeners associated with this Container.
	 * If this Container has no registered container listeners, a zero-length
	 * array is returned.
	 */
	@Override
	public ContainerListener[] findContainerListeners() {
		ContainerListener[] results =
				new ContainerListener[0];
		return listeners.toArray(results);
	}


	/**
	 * Process the specified Request, to produce the corresponding Response,
	 * by invoking the first Valve in our pipeline (if any), or the basic
	 * Valve otherwise.
	 *
	 * @param request  Request to be processed
	 * @param response Response to be produced
	 * @throws IllegalStateException if neither a pipeline or a basic
	 *                               Valve have been configured for this Container
	 * @throws IOException           if an input/output error occurred while
	 *                               processing
	 * @throws ServletException      if a ServletException was thrown
	 *                               while processing this request
	 */
	@Override
	public void invoke(Request request, Response response)
			throws IOException, ServletException {

		pipeline.getFirst().invoke(request, response);

	}


	/**
	 * Remove an existing child Container from association with this parent
	 * Container.
	 *
	 * @param child Existing child Container to be removed
	 */
	@Override
	public void removeChild(Container child) {

		if (child == null) {
			return;
		}

		try {
			if (child.getState().isAvailable()) {
				child.stop();
			}
		} catch (LifecycleException e) {
			log.error("ContainerBase.removeChild: stop: ", e);
		}

		try {
			// child.destroy() may have already been called which would have
			// triggered this call. If that is the case, no need to destroy the
			// child again.
			if (!LifecycleState.DESTROYING.equals(child.getState())) {
				child.destroy();
			}
		} catch (LifecycleException e) {
			log.error("ContainerBase.removeChild: destroy: ", e);
		}

		synchronized (children) {
			if (children.get(child.getName()) == null)
				return;
			children.remove(child.getName());
		}

		fireContainerEvent(REMOVE_CHILD_EVENT, child);
	}


	/**
	 * Remove a container event listener from this component.
	 *
	 * @param listener The listener to remove
	 */
	@Override
	public void removeContainerListener(ContainerListener listener) {
		listeners.remove(listener);
	}


	/**
	 * Remove a property change listener from this component.
	 *
	 * @param listener The listener to remove
	 */
	@Override
	public void removePropertyChangeListener(PropertyChangeListener listener) {

		support.removePropertyChangeListener(listener);

	}


	/**
	 * 主要用于初始化ThreadPoolExecoutor（管理线程）类型的startStopExecutor属性，用于管理启动和关闭线程
	 */
	@Override
	protected void initInternal() throws LifecycleException {
		BlockingQueue<Runnable> startStopQueue =
				new LinkedBlockingQueue<Runnable>();
		startStopExecutor = new ThreadPoolExecutor(
				getStartStopThreadsInternal(),
				getStartStopThreadsInternal(), 10, TimeUnit.SECONDS,
				startStopQueue,
				new StartStopThreadFactory(getName() + "-startStop-"));
		startStopExecutor.allowCoreThreadTimeOut(true);
		super.initInternal();
	}


	/**
	 * 启动，作了5件事。
	 * 1，如果有Manager,Cluster,Realm和DirContext则调用start方法。
	 * 2，调用所有子容器的start方法启动子容器
	 * 3，调用管道中Value的start方法启动子容器
	 * 4，启动完成后将生命周期状态设置为LifecycleState.STARTING状态
	 * 5，启用后台线程定时处理一些事情
	 */
	@Override
	protected synchronized void startInternal() throws LifecycleException {

		// Start our subordinate components, if any
		Loader loader = getLoaderInternal();
		if ((loader != null) && (loader instanceof Lifecycle))
			((Lifecycle) loader).start();
		logger = null;
		getLogger();

		//如果有Manager,Cluster,Realm和DirContext则调用start方法。
		Manager manager = getManagerInternal();
		if ((manager != null) && (manager instanceof Lifecycle))
			((Lifecycle) manager).start();
		Cluster cluster = getClusterInternal();
		if ((cluster != null) && (cluster instanceof Lifecycle))
			((Lifecycle) cluster).start();
		Realm realm = getRealmInternal();
		if ((realm != null) && (realm instanceof Lifecycle))
			((Lifecycle) realm).start();
		DirContext resources = getResourcesInternal();
		if ((resources != null) && (resources instanceof Lifecycle))
			((Lifecycle) resources).start();

		// Start our child containers, if any
		//获取所有的子容器
		Container children[] = findChildren();
		List<Future<Void>> results = new ArrayList<Future<Void>>();
		for (int i = 0; i < children.length; i++) {
			//这里通过线程调用子容器start方法，相当于childrean[i].start();
			results.add(startStopExecutor.submit(new StartChild(children[i])));//多线程
			//children[i].start();//单线程
		}

		MultiThrowable multiThrowable = null;

		//处理子容器启动线程的Future
		for (Future<Void> result : results) {
			try {
				result.get();//阻塞
			} catch (Throwable e) {
				log.error(sm.getString("containerBase.threadedStartFailed"), e);
				if (multiThrowable == null) {
					multiThrowable = new MultiThrowable();
				}
				multiThrowable.add(e);
			}

		}
		if (multiThrowable != null) {
			throw new LifecycleException(sm.getString("containerBase.threadedStartFailed"),
					multiThrowable.getThrowable());
		}

		// Start the Valves in our pipeline (including the basic), if any
		if (pipeline instanceof Lifecycle) {
			//启动管道
			((Lifecycle) pipeline).start();
		}

		//启动完成后将生命周期状态设置为LifecycleState.STARTING状态
		setState(LifecycleState.STARTING);

		// Start our thread
		//启动后台线程
		threadStart();
	}


	/**
	 * Stop this component and implement the requirements
	 * of {@link org.apache.catalina.util.LifecycleBase#stopInternal()}.
	 *
	 * @throws LifecycleException if this component detects a fatal error
	 *                            that prevents this component from being used
	 */
	@Override
	protected synchronized void stopInternal() throws LifecycleException {

		// Stop our thread
		threadStop();

		setState(LifecycleState.STOPPING);

		// Stop the Valves in our pipeline (including the basic), if any
		if (pipeline instanceof Lifecycle &&
				((Lifecycle) pipeline).getState().isAvailable()) {
			((Lifecycle) pipeline).stop();
		}

		// Stop our child containers, if any
		Container children[] = findChildren();
		List<Future<Void>> results = new ArrayList<Future<Void>>();
		for (int i = 0; i < children.length; i++) {
			results.add(startStopExecutor.submit(new StopChild(children[i])));
		}

		boolean fail = false;
		for (Future<Void> result : results) {
			try {
				result.get();
			} catch (Exception e) {
				log.error(sm.getString("containerBase.threadedStopFailed"), e);
				fail = true;
			}
		}
		if (fail) {
			throw new LifecycleException(
					sm.getString("containerBase.threadedStopFailed"));
		}

		// Stop our subordinate components, if any
		DirContext resources = getResourcesInternal();
		if ((resources != null) && (resources instanceof Lifecycle)) {
			((Lifecycle) resources).stop();
		}
		Realm realm = getRealmInternal();
		if ((realm != null) && (realm instanceof Lifecycle)) {
			((Lifecycle) realm).stop();
		}
		Cluster cluster = getClusterInternal();
		if ((cluster != null) && (cluster instanceof Lifecycle)) {
			((Lifecycle) cluster).stop();
		}
		Manager manager = getManagerInternal();
		if ((manager != null) && (manager instanceof Lifecycle) &&
				((Lifecycle) manager).getState().isAvailable()) {
			((Lifecycle) manager).stop();
		}
		Loader loader = getLoaderInternal();
		if ((loader != null) && (loader instanceof Lifecycle)) {
			((Lifecycle) loader).stop();
		}
	}

	@Override
	protected void destroyInternal() throws LifecycleException {

		Manager manager = getManagerInternal();
		if ((manager != null) && (manager instanceof Lifecycle)) {
			((Lifecycle) manager).destroy();
		}
		Realm realm = getRealmInternal();
		if ((realm != null) && (realm instanceof Lifecycle)) {
			((Lifecycle) realm).destroy();
		}
		Cluster cluster = getClusterInternal();
		if ((cluster != null) && (cluster instanceof Lifecycle)) {
			((Lifecycle) cluster).destroy();
		}
		Loader loader = getLoaderInternal();
		if ((loader != null) && (loader instanceof Lifecycle)) {
			((Lifecycle) loader).destroy();
		}

		// Stop the Valves in our pipeline (including the basic), if any
		if (pipeline instanceof Lifecycle) {
			((Lifecycle) pipeline).destroy();
		}

		// Remove children now this container is being destroyed
		for (Container child : findChildren()) {
			removeChild(child);
		}

		// Required if the child is destroyed directly.
		if (parent != null) {
			parent.removeChild(this);
		}

		// If init fails, this may be null
		if (startStopExecutor != null) {
			startStopExecutor.shutdownNow();
		}

		super.destroyInternal();
	}


	/**
	 * Check this container for an access log and if none is found, look to the
	 * parent. If there is no parent and still none is found, use the NoOp
	 * access log.
	 */
	@Override
	public void logAccess(Request request, Response response, long time,
						  boolean useDefault) {

		boolean logged = false;

		if (getAccessLog() != null) {
			getAccessLog().log(request, response, time);
			logged = true;
		}

		if (getParent() != null) {
			// No need to use default logger once request/response has been logged
			// once
			getParent().logAccess(request, response, time, (useDefault && !logged));
		}
	}

	@Override
	public AccessLog getAccessLog() {

		if (accessLogScanComplete) {
			return accessLog;
		}

		AccessLogAdapter adapter = null;
		Valve valves[] = getPipeline().getValves();
		for (Valve valve : valves) {
			if (valve instanceof AccessLog) {
				if (adapter == null) {
					adapter = new AccessLogAdapter((AccessLog) valve);
				} else {
					adapter.add((AccessLog) valve);
				}
			}
		}
		if (adapter != null) {
			accessLog = adapter;
		}
		accessLogScanComplete = true;
		return accessLog;
	}

	// ------------------------------------------------------- Pipeline Methods


	/**
	 * Convenience method, intended for use by the digester to simplify the
	 * process of adding Valves to containers. See
	 * {@link Pipeline#addValve(Valve)} for full details. Components other than
	 * the digester should use {@link #getPipeline()}.{@link #addValve(Valve)} in case a
	 * future implementation provides an alternative method for the digester to
	 * use.
	 *
	 * @param valve Valve to be added
	 * @throws IllegalArgumentException if this Container refused to
	 *                                  accept the specified Valve
	 * @throws IllegalArgumentException if the specified Valve refuses to be
	 *                                  associated with this Container
	 * @throws IllegalStateException    if the specified Valve is already
	 *                                  associated with a different Container
	 */
	public synchronized void addValve(Valve valve) {

		pipeline.addValve(valve);
	}


	/**
	 * Execute a periodic task, such as reloading, etc. This method will be
	 * invoked inside the classloading context of this container. Unexpected
	 * throwables will be caught and logged.
	 * 提供了所有容器共同处理
	 */
	@Override
	public void backgroundProcess() {

		if (!getState().isAvailable())
			return;

		Cluster cluster = getClusterInternal();
		if (cluster != null) {
			try {
				cluster.backgroundProcess();
			} catch (Exception e) {
				log.warn(sm.getString("containerBase.backgroundProcess.cluster", cluster), e);
			}
		}
		Loader loader = getLoaderInternal();
		if (loader != null) {
			try {
				loader.backgroundProcess();
			} catch (Exception e) {
				log.warn(sm.getString("containerBase.backgroundProcess.loader", loader), e);
			}
		}
		Manager manager = getManagerInternal();
		if (manager != null) {
			try {
				manager.backgroundProcess();
			} catch (Exception e) {
				log.warn(sm.getString("containerBase.backgroundProcess.manager", manager), e);
			}
		}
		Realm realm = getRealmInternal();
		if (realm != null) {
			try {
				realm.backgroundProcess();
			} catch (Exception e) {
				log.warn(sm.getString("containerBase.backgroundProcess.realm", realm), e);
			}
		}
		Valve current = pipeline.getFirst();
		while (current != null) {
			try {
				current.backgroundProcess();
			} catch (Exception e) {
				log.warn(sm.getString("containerBase.backgroundProcess.valve", current), e);
			}
			current = current.getNext();
		}
		fireLifecycleEvent(Lifecycle.PERIODIC_EVENT, null);
	}


	// ------------------------------------------------------ Protected Methods


	/**
	 * Notify all container event listeners that a particular event has
	 * occurred for this Container.  The default implementation performs
	 * this notification synchronously using the calling thread.
	 *
	 * @param type Event type
	 * @param data Event data
	 */
	@Override
	public void fireContainerEvent(String type, Object data) {

		if (listeners.size() < 1)
			return;

		ContainerEvent event = new ContainerEvent(this, type, data);
		// Note for each uses an iterator internally so this is safe
		for (ContainerListener listener : listeners) {
			listener.containerEvent(event);
		}
	}


	/**
	 * Return the abbreviated name of this container for logging messages
	 */
	protected String logName() {

		if (logName != null) {
			return logName;
		}
		String loggerName = null;
		Container current = this;
		while (current != null) {
			String name = current.getName();
			if ((name == null) || (name.equals(""))) {
				name = "/";
			} else if (name.startsWith("##")) {
				name = "/" + name;
			}
			loggerName = "[" + name + "]"
					+ ((loggerName != null) ? ("." + loggerName) : "");
			current = current.getParent();
		}
		logName = ContainerBase.class.getName() + "." + loggerName;
		return logName;

	}


	// -------------------- JMX and Registration  --------------------

	@Override
	protected String getDomainInternal() {
		return MBeanUtils.getDomain(this);
	}

	public ObjectName[] getChildren() {
		List<ObjectName> names = new ArrayList<ObjectName>(children.size());
		Iterator<Container> it = children.values().iterator();
		while (it.hasNext()) {
			Object next = it.next();
			if (next instanceof ContainerBase) {
				names.add(((ContainerBase) next).getObjectName());
			}
		}
		return names.toArray(new ObjectName[names.size()]);
	}


	// -------------------- Background Thread --------------------

	/**
	 * Start the background thread that will periodically check for
	 * session timeouts.
	 * 后台线程是一个while循环，内部调用backgroundProcessor方法作一些事情
	 * 间隔的长短是通过ContainerBase的backgroundProcessorDelay属性类设置，单位为'秒'
	 * 如果backgroundProcessorDelay<0 不启用线程
	 * 不过其如果backgroundProcessor方法会在父容器的后台线程中调用。
	 */
	protected void threadStart() {

		if (thread != null)
			return;
		//如果backgroundProcessorDelay<0 不启用线程
		if (backgroundProcessorDelay <= 0)
			return;

		threadDone = false;
		String threadName = "ContainerBackgroundProcessor[" + toString() + "]";
		thread = new Thread(new ContainerBackgroundProcessor(), threadName);
		//守护线程
		thread.setDaemon(true);
		thread.start();

	}


	/**
	 * Stop the background thread that is periodically checking for
	 * session timeouts.
	 */
	protected void threadStop() {

		if (thread == null)
			return;

		threadDone = true;
		thread.interrupt();
		try {
			thread.join();
		} catch (InterruptedException e) {
			// Ignore
		}

		thread = null;

	}


	// -------------------------------------- ContainerExecuteDelay Inner Class


	/**
	 * Private thread class to invoke the backgroundProcess method
	 * of this container and its children after a fixed delay.
	 */
	protected class ContainerBackgroundProcessor implements Runnable {

		@Override
		public void run() {
			Throwable t = null;
			String unexpectedDeathMessage = sm.getString(
					"containerBase.backgroundProcess.unexpectedThreadDeath",
					Thread.currentThread().getName());
			try {
				while (!threadDone) {
					try {
						Thread.sleep(backgroundProcessorDelay * 1000L);
					} catch (InterruptedException e) {
						// Ignore
					}
					if (!threadDone) {
						Container parent = (Container) getMappingObject();
						ClassLoader cl =
								Thread.currentThread().getContextClassLoader();
						if (parent.getLoader() != null) {
							cl = parent.getLoader().getClassLoader();
						}
						processChildren(parent, cl);
					}
				}
			} catch (RuntimeException e) {
				t = e;
				throw e;
			} catch (Error e) {
				t = e;
				throw e;
			} finally {
				if (!threadDone) {
					log.error(unexpectedDeathMessage, t);
				}
			}
		}

		protected void processChildren(Container container, ClassLoader cl) {
			try {
				if (container.getLoader() != null) {
					Thread.currentThread().setContextClassLoader
							(container.getLoader().getClassLoader());
				}
				//调用 backgroundProcess
				container.backgroundProcess();
			} catch (Throwable t) {
				ExceptionUtils.handleThrowable(t);
				log.error("Exception invoking periodic operation: ", t);
			} finally {
				Thread.currentThread().setContextClassLoader(cl);
			}
			Container[] children = container.findChildren();
			for (int i = 0; i < children.length; i++) {
				if (children[i].getBackgroundProcessorDelay() <= 0) {
					processChildren(children[i], cl);
				}
			}
		}
	}


	// ----------------------------- Inner classes used with start/stop Executor
	/**
	 * 主要作用：调用子容器的sart方法
	 */
	private static class StartChild implements Callable<Void> {

		private Container child;

		public StartChild(Container child) {
			this.child = child;
		}

		@Override
		public Void call() throws LifecycleException {
			child.start();
			return null;
		}
	}

	private static class StopChild implements Callable<Void> {

		private Container child;

		public StopChild(Container child) {
			this.child = child;
		}

		@Override
		public Void call() throws LifecycleException {
			if (child.getState().isAvailable()) {
				child.stop();
			}
			return null;
		}
	}

	private static class StartStopThreadFactory implements ThreadFactory {
		private final ThreadGroup group;
		private final AtomicInteger threadNumber = new AtomicInteger(1);
		private final String namePrefix;

		public StartStopThreadFactory(String namePrefix) {
			SecurityManager s = System.getSecurityManager();
			group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
			this.namePrefix = namePrefix;
		}

		@Override
		public Thread newThread(Runnable r) {
			Thread thread = new Thread(group, r, namePrefix + threadNumber.getAndIncrement());
			thread.setDaemon(true);
			return thread;
		}
	}

}
